package net.cassite.daf4j.jpa;

import net.cassite.daf4j.Selectable;
import net.cassite.daf4j.util.AliasMap;
import net.cassite.daf4j.util.ConstantMap;
import net.cassite.daf4j.util.Location;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.*;

/**
 * JPQL上下文
 */
public class JPQLContext {
        public StringBuilder generalJPQL; // WON'T BE NULL // REFRESH

        // NEVER NULL
        public Object entity;  // REFRESH WHEN SUBQUERYING
        public Class<?> entityClass;  // REFRESH
        public AliasMap aliasMap;  // STAY THE SAME
        public ConstantMap constantMap; // STAY THE SAME
        public Map<Location, String> toJoin; // REFRESH

        public ConstantMap protectedConstantMap; // WILL BE NULL OUT OF WHERE PARSING STEP

        public boolean NoDistinctOrAlreadyDone; // ONLY REFER THIS WHEN PARSING SELECT
        public boolean GeneratingWhere; // REFRESH
        public boolean RequireGroupBy; // REFRESH

        public EntityManager manager; // STAY
        public Query jpaQuery;  // WILL BE NULL UNTIL JPQL GENERATION COMPLETE // WILL BE NULL WHEN SUBQUERYING

        public StringBuilder frontQueries; // WILL BE NULL UNLESS IT'S LIST/PROJECTION
        public List<String> selectNonAggregationAliases; // NOT NULL, MAY BE EMPTY LIST // REFRESH
        public String havingClause; // NULL UNTIL FINISHED GENERATING WHERE, AND WILL BE NULL WHEN GENERATION COMPLETE

        public List<JPQLToDo<JPQLContext>> toDoAfterJPQLGeneration;  // NOT NULL // REFRESH
        public List<JPQLToDo<JPQLContext>> toDoAfterJPAQueryGeneration; // NOT NULL // WON'T BE USED WHEN SUBQUERYING, USE NULL IN CASE IT GOES WRONG

        public Map<Selectable, String> focusMap; // ONLY NOT NULL WHEN PARSING PROJECTION

        private String aliasPrefix;

        public JPQLContext(EntityManager manager, Object entity, String aliasPrefix) {
                this.aliasPrefix = aliasPrefix;

                generalJPQL = new StringBuilder();

                this.manager = manager;
                this.entity = entity;
                this.entityClass = entity.getClass();
                this.aliasMap = new AliasMap(aliasPrefix);

                constantMap = new ConstantMap();
                toJoin = new LinkedHashMap<Location, String>();

                GeneratingWhere = false;

                selectNonAggregationAliases = new ArrayList<String>();
                toDoAfterJPQLGeneration = new ArrayList<JPQLToDo<JPQLContext>>();
                toDoAfterJPAQueryGeneration = new ArrayList<JPQLToDo<JPQLContext>>();
        }

        private JPQLContext() {
                this.generalJPQL = new StringBuilder();
                this.toJoin = new HashMap<Location, String>();
                this.selectNonAggregationAliases = new ArrayList<String>();
                this.GeneratingWhere = false;
                this.toDoAfterJPQLGeneration = new ArrayList<JPQLToDo<JPQLContext>>();
        }

        public JPQLContext copyForSubQuery(Object entity, boolean NoDistinctOrAlreadyDone) {
                JPQLContext newContext = new JPQLContext();

                newContext.entity = entity;
                newContext.entityClass = newContext.entity.getClass();
                newContext.aliasMap = new AliasMap(aliasPrefix);
                newContext.aliasMap.setAliasCount(this.aliasMap.getAliasCount());
                newContext.constantMap = this.constantMap;
                newContext.NoDistinctOrAlreadyDone = NoDistinctOrAlreadyDone;
                newContext.manager = this.manager;

                return newContext;
        }
}
